// c++版本
#include <iostream>
#include <cstdlib>
#include <vector>
#include <cmath>
#include <algorithm>
#include <fstream>
#include <string>

using namespace std;
class NetWork
{
private:
	int batch_size = 8;
	int input_length = 100;
	int hidden_length = 100;
	int output_length = 100;

	// 优化器参数
	double lr = 1e-3;
	double beta1 = 0.9;
	double beta2 = 0.999;
	double eps=1e-8;

	/*
		y_hat = softmax(relu(input[b*f]*weight1[f*h])*weight2[h*c])
		hidden[b*h] : input[b*f]*weight1[f*h]
		hidden2[b*h]: relu(hidden[b*h])
		output[b*c] : hidden2[b*h]*weight2[h*c]
		y_hat[b*c]  : softmax(output[b*c])
	*/
	vector<vector<double>> hidden;
	vector<vector<double>> hidden2;
	vector<vector<double>> output;
	vector<vector<double>> y_hat;

	vector<vector<double>> weight1;
	vector<vector<double>> weight2;

	// 导数部分
	vector<vector<double>> d_weight1;
	vector<vector<double>> d_weight2;
	vector<vector<double>> d_hidden;
	vector<vector<double>> d_hidden2;
	vector<vector<double>> d_output;

	// adam 优化器 历史导数
	vector<vector<double>> weight1_m;
	vector<vector<double>> weight2_m;
	vector<vector<double>> weight1_v;
	vector<vector<double>> weight2_v;

public:
	NetWork(){
		hidden = vector<vector<double>>(440,vector<double>(hidden_length));
		srand((int)time(0));//随机初始化
		for (int i = 0;i<input_length;++i){
			for(int j = 0;j<hidden_length;++j){
				weight1[i][j] = 1.0*(rand()%1000-500)/10000;
			}
		}
		for (int i = 0;i<hidden_length;++i){
			for(int j = 0;j<output_length;++j){
				weight2[i][j] = 1.0*(rand()%1000-500)/10000;
			}
		}
	}

	vector<vector<double>> forward(vector<vector<double>> &input){
		// batch_size * feature_size(input_length) 
		// 两层神经网络简洁实现
		hidden = multiply(input,weight1);
		hidden2 = relu(hidden);
		output = multiply(hidden2,weight2);
		y_hat = softmax(output);
		return y_hat;
	}

	void backward(vector<vector<double>> &y_hat,vector<vector<double>> &y){
		// 反向传播,计算导数。此时的y是one_hot向量
		// 采用交叉熵损失函数 ylog(y_hat)
		for (int i = 0;i<batch_size;++i){
			for (int j = 0;j<output_length;++j){
				d_output[i][j] = y_hat[i][j] - y[i][j];
			}
		}
		d_hidden2 = multiply(d_output,transpose(weight2));
		d_weight2 = multiply(transpose(hidden2),d_output);
		for (int i = 0;i<batch_size;++i){
			for (int j = 0;j<hidden_length;++j){
				d_hidden[i][j] = hidden[i][j]>=0 ? d_hidden2[i][j]:0;
			}
		}
		d_weight1 = multiply(transpose(input),d_hidden);
	}

	void update(int t){// 更新参数(weight1和weight2)
		weight1_m = add(multiply(beta1,weight1_m),multiply(1-beta1,d_weight1));
		weight1_v = add(multiply(beta2,weight1_v),multiply(1-beta2,multiply_dot(d_weight1,d_weight1)));
		vector<vector<double>> weight1_m_hat = multiply(1.0/(1-pow(beta1,t)),weight1_m);
		vector<vector<double>> weight1_v_hat = multiply(1.0/(1-pow(beta2,t)),weight1_v);
		weight1 = add(weight1,div(multiply(-1*lr,weight1_m_hat) , add(eps,sqrt(weight1_v_hat))));

		weight2_m = add(multiply(beta1,weight2_m),multiply(1-beta1,d_weight2));
		weight2_v = add(multiply(beta2,weight2_v),multiply(1-beta2,multiply_dot(d_weight2,d_weight2)));
		vector<vector<double>> weight2_m_hat = multiply(1.0/(1-pow(beta1,t)),weight2_m);
		vector<vector<double>> weight2_v_hat = multiply(1.0/(1-pow(beta2,t)),weight2_v);
		weight1 = add(weight1,div(multiply(-1*lr,weight2_m_hat) , add(eps,sqrt(weight2_v_hat))));
	}

	// utils 工具函数

	// 矩阵乘法
	vector<vector<double>> multiply(vector<vector<double>> &arr1,vector<vector<double>> &arr2){
		// 这个地方以后可以改成多线程
		// [m * k] * [k * n]
		int m = arr1.size();
		int k = arr2.size();
		int n = arr2[0].size();
		vector<vector<double>> ans(m,vector<double>(n));
		for (int i = 0;i<m;++i){
			for (int j = 0;j<n;++j){
				double tmp = 0;
				for (int w = 0;w<k;++w){
					tmp += arr1[i][w] * arr2[w][j];
				}
				ans[i][j] = tmp;
			}
		}
		return ans;
	}
	// 矩阵乘法
	vector<vector<double>> multiply_dot(vector<vector<double>> &arr1,vector<vector<double>> &arr2){
		// 这个地方以后可以改成多线程
		// [m * k] * [k * n]
		int m = arr1.size();
		int n = arr1[0].size();
		vector<vector<double>> ans(m,vector<double>(n));
		for (int i = 0;i<m;++i){
			for (int j = 0;j<n;++j){
				ans[i][j] = arr1[i][j]*arr2[i][j];
			}
		}
		return ans;
	}
	// 数乘
	vector<vector<double>> multiply(double num,vector<vector<double>> &arr){
		int m = arr.size();
		int n = arr[0].size();
		vector<vector<double>> ans(m,vector<double>(n));
		for (int i = 0;i<m;++i){
			for (int j = 0;j<n;++j){
				ans[i][j] = num*arr[i][j];
			}
		}
		return ans;
	}
	vector<vector<double>> add(vector<vector<double>> &arr1,vector<vector<double>> &arr2){
		// 这个地方以后可以改成多线程
		// [m * k] * [k * n]
		int m = arr1.size();
		int n = arr1[0].size();
		vector<vector<double>> ans(m,vector<double>(n));
		for (int i = 0;i<m;++i){
			for (int j = 0;j<n;++j){
				ans[i][j] = arr1[i][j] + arr2[i][j];
			}
		}
		return ans;
	}
	vector<vector<double>> add(double num,vector<vector<double>> &arr){
		// 这个地方以后可以改成多线程
		// [m * k] * [k * n]
		int m = arr.size();
		int n = arr[0].size();
		vector<vector<double>> ans(m,vector<double>(n));
		for (int i = 0;i<m;++i){
			for (int j = 0;j<n;++j){
				ans[i][j] = arr[i][j] + num;
			}
		}
		return ans;
	}
	vector<vector<double>> sqrt(vector<vector<double>> &arr){
		// 这个地方以后可以改成多线程
		// [m * k] * [k * n]
		int m = arr.size();
		int n = arr[0].size();
		vector<vector<double>> ans(m,vector<double>(n));
		for (int i = 0;i<m;++i){
			for (int j = 0;j<n;++j){
				ans[i][j] = pow(arr[i][j],0.5);
			}
		}
		return ans;
	}//div
	vector<vector<double>> div(vector<vector<double>> &arr1,vector<vector<double>> &arr2){
		// 这个地方以后可以改成多线程
		// [m * k] * [k * n]
		int m = arr1.size();
		int n = arr1[0].size();
		vector<vector<double>> ans(m,vector<double>(n));
		for (int i = 0;i<m;++i){
			for (int j = 0;j<n;++j){
				ans[i][j] = arr1[i][j] / arr2[i][j];
			}
		}
		return ans;
	}
	vector<vector<double>> transpose(vector<vector<double>> arr){
		int size1 = arr.size();
		int size2 = arr[0].size();
		vector<vector<double>> ans(size2,vector<double>(size1));
		for (int i = 0;i<size1;++i){
			for (int j = 0;j<size2;++j){
				ans[j][i] = arr[i][j];
			}
		}
		return ans;
	}

	void relu(vector<vector<double>> &arr){
		// 这个地方也是
		int size1 = arr.size();
		for (int i = 0;i<size1;++i){
			int size2 = arr[0].size();
			for (int j = 0;j<size2;++j){
				arr[i][j] = max(arr[i][j],0);
			}
		}
	}
	void softmax(vector<vector<double>> &w){
		int size1 = w.size(); // batch_size,output_length
		for (int i = 0;i<size1;++i){
			int size2 = w[0].size();
			double sum = 0;
			for (int j = 0;j<size2;++j){
				w[i][j] = exp(max(w[i][j],20));
				sum += w[i][j];
			}
			for (int j = 0;j<size2;++j){
				w[i][j] /= sum;
			}
		}
	}
};

void read_data(vector<vector<double>> &train_x,
			   vector<vector<double>> &train_y,
			   vector<vector<double>> &test_x,
			   vector<vector<double>> &test_y){
	ifstream input;
    input.open("数据集/测试集/f0.dat");

    unsigned char h1,h2;

    do
    {
        input >> h1;
        cout<<(int) h1<<endl;
    }while(!input.eof());

	cin.get();
}

int main(int argc, char const *argv[])
{
	vector<vector<double>> train_x;
	vector<vector<double>> test_x;
	vector<vector<double>> train_y;
	vector<vector<double>> test_y;
	read_data(train_x,train_y,test_x,test_y);
	return 0;
}